/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.openmaximo.maven.plugins.templates;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.HashMap;
import java.util.StringTokenizer;

import net.openmaximo.log.Log4jLogger;

import org.apache.log4j.Logger;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.InterpolatorFilterReader;
import org.codehaus.plexus.interpolation.MapBasedValueSource;
import org.codehaus.plexus.interpolation.StringSearchInterpolator;

/**
 * <p>
 * Creates a new class applying template and substitutions.
 * </p>
 * 
 * @author openmaximo.net
 * @version 1.0
 */
public class Template {
  /**
   * 
   */
  private static final Logger lLogger = Log4jLogger.getLogger(Template.class);
  /**
   * 
   */
  private InputStreamReader templateISR;
  /**
   * 
   */
  private OutputStreamWriter classOSW;
  /**
   * 
   */
  private HashMap patternsHM;

  /**
   * <p>
   * </p>
   * 
   * @param refClass
   *          Parent class that is calling the template.
   * @param templateFileName
   *          Name of the template file.
   * @param outputFolder
   *          Output folder.
   * @param outputClassName
   *          Output class name.
   * @param charEncoding
   *          Character encoding to be used.
   * @param patternsHM
   *          Patterns to be replaced.
   * @throws IOException
   */
  public Template(Class refClass, String templateFileName, String outputFolder,
      String outputClassName, String charEncoding, HashMap patternsHM)
      throws IOException {
    lLogger
        .info(" Template(Class,String,String,String,String,HashMap) started...");
    if (lLogger.isDebugEnabled()) {
      lLogger.debug(" refClass:" + refClass);
      lLogger.debug(" inputFileName:" + templateFileName);
      lLogger.debug(" outputFolder:" + outputFolder);
      lLogger.debug(" className:" + outputClassName);
      lLogger.debug(" charEncoding:" + charEncoding);
      lLogger.debug(" patternsHM:" + patternsHM);
    }
    // Create output folder;
    File outFolder = new File(outputFolder);
    outFolder.mkdirs();
    // Obtain Input Stream Reader
    URL url = null;
    String inputFilePath = "/templates/".concat(refClass.getSimpleName()
        .concat("/").concat(templateFileName));
    url = refClass.getResource(inputFilePath);
    if (lLogger.isDebugEnabled()) {
      lLogger.debug(" inputFilePath:" + inputFilePath);
      lLogger.debug(" url:" + url);
    }
    this.templateISR = new InputStreamReader(url.openStream());
    // Obtain Output Stream Writer
    String outputFile = outputFolder.concat("/").concat(outputClassName)
        .concat(".java");
    url = refClass.getResource(outputFile);
    FileOutputStream outFOS = new FileOutputStream(outputFile);
    this.classOSW = new OutputStreamWriter(outFOS, charEncoding);
    this.patternsHM = patternsHM;
    lLogger
        .info(" Template(Class,String,String,String,String,HashMap) finished.");
  }

  /**
   * @param templateISR
   * @param mboOSR
   * @param patternsHM
   * @deprecated
   */
  public Template(InputStreamReader templateISR, OutputStreamWriter mboOSR,
      HashMap patternsHM) {
    this.templateISR = templateISR;
    this.classOSW = mboOSR;
    this.patternsHM = patternsHM;
  }

  /**
   * <p>
   * Applies template and creates new class.
   * </p>
   */
  public void applyTemplate() {
    lLogger.info(".applyTemplate() started...");
    BufferedReader templateBR = new BufferedReader(templateISR);
    BufferedWriter mboBW = new BufferedWriter(classOSW);
    // Line red
    String lineRedS;
    String lineToWriteS;
    StringSearchInterpolator propertiesInterpolator = new StringSearchInterpolator();
    MapBasedValueSource valueSource = new MapBasedValueSource(patternsHM);
    propertiesInterpolator.addValueSource(valueSource);
    InterpolatorFilterReader interpolatorFilterReader = new InterpolatorFilterReader(
        templateISR, propertiesInterpolator);
    interpolatorFilterReader.setInterpolateWithPrefixPattern(false);
    // Pattern propsPattern = Pattern.compile("\\$\\{.*\\}");
    try {
      lineRedS = templateBR.readLine();
      while (lineRedS != null) {
        lineToWriteS = propertiesInterpolator.interpolate(lineRedS);
        if (lLogger.isDebugEnabled()) {
          lLogger.debug(lineRedS);
          lLogger.debug(lineToWriteS);
        }
        mboBW.write(lineToWriteS);
        mboBW.newLine();
        lineRedS = templateBR.readLine();
      }
    } catch (IOException e) {
      // I don't care if there is an exception at this point.
      e.printStackTrace();
    } catch (InterpolationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } finally {
      if (templateBR != null) {
        try {
          templateBR.close();
        } catch (IOException e) {
          // I don't care if there is an exception at this point.
          e.printStackTrace();
        }
      }
      if (mboBW != null) {
        try {
          mboBW.close();
        } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }
    lLogger.info(".applyTemplate() finished.");
  }

  /**
   * <p>
   * Builds the pattern HashMap.
   * </p>
   * 
   * @param className
   *          Name of the to be passed to the template file.
   * @param parentClass
   * @param classPackage
   * @param objectClassName
   * @param overrideMethods
   * @param refClass
   * @return
   */
  public static HashMap setPatterns(String className, String parentClass,
      String classPackage, String objectClassName, String overrideMethods,
      Class refClass) {
    lLogger
        .info(".setPatterns(String,String,String,String,String,Class) started...");
    if (lLogger.isDebugEnabled()) {
      lLogger.debug(" className:" + className);
      lLogger.debug(" parentClass:" + overrideMethods);
      lLogger.debug(" classPackage:" + classPackage);
      lLogger.debug(" objectClassName:" + objectClassName);
      lLogger.debug(" overrideMethods:" + overrideMethods);
      lLogger.debug(" refClass:" + refClass);
    }
    HashMap patternsHM = new HashMap();
    patternsHM.put("className", className);
    patternsHM.put("parentClass", parentClass);
    patternsHM.put("package", classPackage);
    // Methods
    // Obtain Input Stream Reader
    URL url = null;
    // TODO finish
    StringBuffer strBuff = new StringBuffer();
    if (overrideMethods != null) {
      StringTokenizer processParametersST = null;
      processParametersST = new StringTokenizer(overrideMethods, ",");
      boolean has_more_tokens = processParametersST.hasMoreTokens();
      while (has_more_tokens) {
        String methodName = processParametersST.nextToken();
        String inputFilePath = "/templates/".concat(objectClassName)
            .concat("/").concat(methodName).concat(".template");
        url = refClass.getResource(inputFilePath);
        BufferedReader templateBR = null;
        try {
          InputStreamReader templateISR = new InputStreamReader(url
              .openStream());
          templateBR = new BufferedReader(templateISR);
          String lineRedS = templateBR.readLine();
          while (lineRedS != null) {
            if (lLogger.isDebugEnabled()) {
              lLogger.debug(lineRedS);
            }
            strBuff.append(lineRedS);
            strBuff.append("\n");
            lineRedS = templateBR.readLine();
          }
        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          if (templateBR != null) {
            try {
              templateBR.close();
            } catch (IOException e) {
              // I don't care if there is an exception at this point.
              e.printStackTrace();
            }
          }
        }
        has_more_tokens = processParametersST.hasMoreTokens();
      } // while
    }
    patternsHM.put("methods", strBuff);
    lLogger
        .info(".setPatterns(String,String,String,String,String,Class) finished.");
    return patternsHM;
  }
}
